package com.tianji.learning.service.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
 * <p>
 *
 * </p>
 *
 * @author taohongrun
 * @since 2025/2/11
 */
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {

    private final StringRedisTemplate redisTemplate;
    private  final RabbitTemplate rabbitTemplate;
    @Override
    public SignResultVO addSignRecords() {
        // 获取当前操作用户
        Long user = UserContext.getUser();
        // 检查用户是否已登录
        if (user == null) throw new BadRequestException("用户未登录");
        // 获取签到密钥
        String key = signKey(user, LocalDate.now());
        // 确定当前天数
        LocalDate now = LocalDate.now();
        int day = now.getDayOfMonth();
        // 在Redis中设置签到状态位
        Boolean b = redisTemplate.opsForValue().setBit(key, day - 1, true);
        // 检查是否重复签到
        if (BooleanUtil.isTrue(b)) throw new BadRequestException("重复签到");
        // 获取连续签到天数
        Integer continuousSignDays = getContinuousSignDays(user, now);
        // 创建签到结果对象
        SignResultVO signResultVO = new SignResultVO();
        // 设置连续签到奖励积分
        Integer rewardPoints = getContinuousSignPoints(continuousSignDays);
        signResultVO.setRewardPoints(rewardPoints);
        // 设置本次签到获得的积分
        signResultVO.setSignPoints(1);
        // 设置连续签到天数
        signResultVO.setSignDays(continuousSignDays);

        //发送消息，保存积分记录
        rabbitTemplate.convertAndSend(MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(user,rewardPoints + 1));
        // 返回签到结果
        return signResultVO;

    }

    @Override
    public List<Byte> querySignRecords() {
        // 获取当前操作用户
        Long user = UserContext.getUser();
        // 检查用户是否已登录
        if (user == null) throw new BadRequestException("用户未登录");
        //获取基本参数
        LocalDate now = LocalDate.now();
        String  key = signKey(user, now);
        int day = now.getDayOfMonth();

        //获取无符号整数签到结果
        List<Long> longs = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0));
        if (CollUtils.isEmpty(longs)) return CollUtil.empty(Byte.class);
        int num = longs.get(0).intValue();

        //移位并倒序为数组赋值
        List<Byte> signList = new ArrayList<>(Collections.nCopies(day, (byte) 0));
        for(int i = day-1 ; i>=0 ; i--){
            if((num&1) == 1)  signList.set(i, (byte) 1);
            num = num >>> 1;
        }
        return signList;
    }

    public String signKey(Long userId, LocalDate date) {
        String dateString = date.format(DateTimeFormatter.ofPattern("yyyyMM"));
        return StrUtil.format(RedisConstants.SIGN_KEY, userId, dateString);
    }

    public Integer getContinuousSignDays(Long userId, LocalDate date) {
        // 生成用于Redis操作的唯一键
        String key = signKey(userId, date);
        // 使用Redis的bitField命令获取特定日期的用户行为数据
        List<Long> result = redisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(date.getDayOfMonth())).valueAt(0)
        );
        // 检查结果是否为空，如果为空则返回0
        if (CollUtils.isEmpty(result)) {
            return 0;
        }
        // 初始化计数器
        int count = 0;
        // 获取签到数据二进制字符串的十进制结果
        Long num = result.get(0);
        // 循环检查数字的每一位，统计符合条件的行为次数
        while ((num & 1) != 0) {
            count++;
            // 右移一位，继续检查下一位
            num = num >>> 1;
        }
        // 返回统计结果
        return count;
    }

    public Integer getContinuousSignPoints(Integer continuousSignDays) {
        int rewardPoints = 0;
        switch (continuousSignDays) {
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        return rewardPoints;
    }

}
